package logger

import (
	"fmt"
	"github.com/natefinch/lumberjack"
	"go-gin-init/app/core"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"os"
	"time"
)

var Logger *zap.Logger
var SugaredLogger *zap.SugaredLogger

// InitZap
func InitZap() {

	zapConf := core.Conf.ZapLog

	//获取编码器
	encoder := getEncoder(zapConf)

	level := core.Conf.Server.Mode
	// 设置级别
	logLevel := zap.DebugLevel
	switch level {
	case "debug":
		logLevel = zap.DebugLevel
	case "info":
		logLevel = zap.InfoLevel
	case "warn":
		logLevel = zap.WarnLevel
	case "error":
		logLevel = zap.ErrorLevel
	case "panic":
		logLevel = zap.PanicLevel
	case "fatal":
		logLevel = zap.FatalLevel
	default:
		logLevel = zap.InfoLevel
	}

	//日志级别
	lowPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool { //error级别
		//return lev >= zap.ErrorLevel
		return lev < zapcore.WarnLevel && lev >= logLevel
	})
	highPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool { //info和debug级别,debug级别是最低的
		return lev >= zapcore.WarnLevel && lev >= logLevel
		//return lev < zap.ErrorLevel && lev >= zap.DebugLevel
	})

	//info文件WriteSyncer
	infoFileWriteSyncer := getInfoWriterSyncer(zapConf)
	//error文件WriteSyncer
	errorFileWriteSyncer := getErrorWriterSyncer(zapConf)

	//同时输出到控制台
	//multiWriteSyncer := zapcore.NewMultiWriteSyncer(writerSyncer, zapcore.AddSync(os.Stdout)) //AddSync将io.Writer转换成WriteSyncer的类型

	// 开发环境同时输出到控制台 和 指定的日志文件中
	var infoSyncer = zapcore.NewMultiWriteSyncer(infoFileWriteSyncer)
	var errorSyncer = zapcore.NewMultiWriteSyncer(errorFileWriteSyncer)
	if core.Conf.Server.Mode == "info" {
		infoSyncer = zapcore.NewMultiWriteSyncer(infoFileWriteSyncer, zapcore.AddSync(os.Stdout))
		errorSyncer = zapcore.NewMultiWriteSyncer(errorFileWriteSyncer, zapcore.AddSync(os.Stdout))
	}

	infoFileCore := zapcore.NewCore(encoder, infoSyncer, lowPriority)
	errorFileCore := zapcore.NewCore(encoder, errorSyncer, highPriority)

	//infoFileCore 和 errorFileCore 加入core切片
	var coreArr []zapcore.Core
	coreArr = append(coreArr, infoFileCore)
	coreArr = append(coreArr, errorFileCore)

	//生成Logger
	Logger = zap.New(zapcore.NewTee(coreArr...), zap.AddCaller()) //zap.AddCaller() 显示文件名 和 行号
	SugaredLogger = Logger.Sugar()

	// 替换zap包中全局的logger实例，后续在其他包中只需使用zap.L()调用即可
	zap.ReplaceGlobals(Logger)

}
func timeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
	enc.AppendString(t.Format("2006-01-02 15:04:05.000"))
}

func levelEncoder(l zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
	var level string
	switch l {
	case zapcore.DebugLevel:
		level = "[DEBUG]"
	case zapcore.InfoLevel:
		level = "[INFO]"
	case zapcore.WarnLevel:
		level = "[WARN]"
	case zapcore.ErrorLevel:
		level = "[ERROR]"
	case zapcore.DPanicLevel:
		level = "[DPANIC]"
	case zapcore.PanicLevel:
		level = "[PANIC]"
	case zapcore.FatalLevel:
		level = "[FATAL]"
	default:
		level = fmt.Sprintf("[LEVEL(%d)]", l)
	}
	enc.AppendString(level)

}

func shortCallerEncoder(caller zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) {
	enc.AppendString(fmt.Sprintf("%s", caller.TrimmedPath()))
}

func NewEncoderConfig() zapcore.EncoderConfig {
	return zapcore.EncoderConfig{
		// Keys can be anything except the empty string.
		TimeKey:       "time",
		LevelKey:      "level",
		NameKey:       "zap",
		CallerKey:     "caller",
		MessageKey:    "message",
		StacktraceKey: "trace",
		LineEnding:    zapcore.DefaultLineEnding,
		EncodeLevel:   zapcore.CapitalLevelEncoder,
		// 自定义 levelEncoder()
		//带颜色 zapcore.CapitalColorLevelEncoder 终端输出有颜色 ，但输出到log文件时，log文件无法解析，会有一小撮文字无法解析（颜色原字符写入）
		EncodeTime:     timeEncoder, //指定时间格式
		EncodeDuration: zapcore.StringDurationEncoder,
		EncodeCaller:   shortCallerEncoder, //zapcore.ShortCallerEncoder,
	}
}

// core 三个参数之  Encoder 获取编码器
func getEncoder(conf *core.ZapLog) zapcore.Encoder {

	//自定义编码配置,
	//下方NewJSONEncoder输出如下的日志格式
	//{"L":"[INFO]","T":"2022-09-16 14:24:59.552","C":"[prototest/main.go:113]","M":"name = xiaoli, age = 18"}
	//return zapcore.NewJSONEncoder(NewEncoderConfig())
	//下方NewConsoleEncoder输出如下的日志格式
	//2022-09-16 14:26:02.933 [INFO]  [prototest/main.go:113] name = xiaoli, age = 18
	//return zapcore.NewConsoleEncoder(NewEncoderConfig())

	// todo 根据环境配置
	// 开发环境
	if core.Conf.Server.Mode == "info" {
		return zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())
	}
	// 生产环境
	return zapcore.NewJSONEncoder(NewEncoderConfig())

	//return zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
	//return zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())

	// 生产环境
	//return zapcore.NewConsoleEncoder(zap.NewProductionEncoderConfig())
	//1.6929537489609218e+09	info	test/10_zap_test.go:51	http响应	{"code  ": 200, "data": "[\"java\"]"}
	// 开发环境
	//return zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())
	//2023-08-25T16:53:59.258+0800	INFO	test/10_zap_test.go:51	http响应	{  "code": 200, "data": "[\"java\"]"}

}

// core 三个参数之  日志输出路径
func getInfoWriterSyncer(conf *core.ZapLog) zapcore.WriteSyncer {
	//file, _ := os.Create("./server/zaplog/log.log")
	//或者将上面的NewMultiWriteSyncer放到这里来，进行返回
	//return zapcore.AddSync(file)

	basePath := conf.SaveBasePath
	//引入第三方库 Lumberjack 加入日志切割功能
	infoLumberIO := &lumberjack.Logger{
		Filename:   basePath + "info.log",
		MaxSize:    10, // megabytes
		MaxBackups: 100,
		MaxAge:     28,    // days
		Compress:   false, //Compress确定是否应该使用gzip压缩已旋转的日志文件。默认值是不执行压缩。
	}
	return zapcore.AddSync(infoLumberIO)
}

func getErrorWriterSyncer(conf *core.ZapLog) zapcore.WriteSyncer {
	basePath := conf.SaveBasePath

	//引入第三方库 Lumberjack 加入日志切割功能
	lumberWriteSyncer := &lumberjack.Logger{
		Filename:   basePath + "error.log",
		MaxSize:    10, // megabytes
		MaxBackups: 100,
		MaxAge:     28,    // days
		Compress:   false, //Compress确定是否应该使用gzip压缩已旋转的日志文件。默认值是不执行压缩。
	}
	return zapcore.AddSync(lumberWriteSyncer)
}

// Debugf 不再封装使用 - 在显示调用者文件名的时候，会全部显示调用者为logger/zaplogger.go - 所以如果要显示调用者文件名和行号，这里的封装就不合适了
// 直接使用 logger.Logger.Info(xxx)
// 或者   logger.SugaredLogger.Infof("xxx%s", name)
// logs.Debug(...) 再封装
func Debugf(format string, v ...interface{}) {
	Logger.Sugar().Debugf(format, v...)
}

func Infof(format string, v ...interface{}) {
	Logger.Sugar().Infof(format, v...)
}

func Warnf(format string, v ...interface{}) {
	Logger.Sugar().Warnf(format, v...)
}

func Errorf(format string, v ...interface{}) {
	Logger.Sugar().Errorf(format, v...)
}

func Panicf(format string, v ...interface{}) {
	Logger.Sugar().Panicf(format, v...)
}

// logs.Debug(...) 再封装
func Debug(format string, fileds ...zapcore.Field) {
	Logger.Debug(format, fileds...)
}

func Info(format string, fileds ...zapcore.Field) {
	Logger.Info(format, fileds...)
}

func Warn(format string, fileds ...zapcore.Field) {
	Logger.Warn(format, fileds...)
}

func Error(format string, fileds ...zapcore.Field) {
	Logger.Error(format, fileds...)
}

func Panic(format string, fileds ...zapcore.Field) {
	Logger.Panic(format, fileds...)
}
